package com.yhy.wxzn.wcs.device.plc.siemens;

import com.alibaba.fastjson.JSON;
import com.google.common.collect.Maps;
import com.yhy.wxzn.common.enums.*;
import com.yhy.wxzn.common.exception.WmsBusinessException;
import com.yhy.wxzn.common.utils.ListHelper;
import com.yhy.wxzn.common.utils.SpringUtil;
import com.yhy.wxzn.wcs.entity.WcsContainerBind;
import com.yhy.wxzn.wcs.entity.WcsLineTask;
import com.yhy.wxzn.wcs.device.plc.bean.*;
import com.yhy.wxzn.wcs.enums.TaskStatus;
import com.yhy.wxzn.wcs.service.IWcsContainerBindService;
import com.yhy.wxzn.wcs.service.IWcsDeviceLogService;
import com.yhy.wxzn.wcs.service.IWcsLineTaskService;
import com.yhy.wxzn.wcs.utils.PlcUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.util.CollectionUtils;

import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * @Auther: 97354
 * @Date: 2023/11/7 09:13
 * @Description: SiemensPlcProtocol
 * @Version 1.0.0
 */
//@Component
@Slf4j
public class SiemensPlcProtocol extends Thread {

    public volatile SiemensPlcDriver siemensPlcDriver;
    private volatile short beatValue = 0;
    private volatile String plcName;
    private IWcsContainerBindService containerBindService;
    private IWcsDeviceLogService deviceLogService;
    private IWcsLineTaskService lineTaskService;

    public SiemensPlcProtocol(String plcName){
        this.plcName = plcName;
    }

    private void init() {
        containerBindService = SpringUtil.getBean(IWcsContainerBindService.class);
        deviceLogService = SpringUtil.getBean(IWcsDeviceLogService.class);
    }

    @Override
    public void run() {
        while (true) {
            try {
                if (SpringUtil.getApplicationContext() != null) {
                    init();
                    break;
                }
                sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        ExecutorService executorService = Executors.newFixedThreadPool(10);
        /**
         * 检测下位机心跳，如果掉线，重连
         * */
        executorService.execute(() -> {
            while (true) {
                try {
                    if (!siemensPlcDriver.siemensHelper.isConnectd()) {
                        sleep(10000);
                        continue;
                    }
                    sleep(1000 * 5);
                    heartbeatProcess();
                    sleep(1000 * 5);
                } catch (Exception ex) {
                    log.error("心跳连接异常", ex);
                }
            }
        });
        /**
         * 给下位机心跳持续写值，检测是否通讯正常
         * */
        executorService.execute(() -> {
            while (true) {
                try {
                    if (!siemensPlcDriver.siemensHelper.isConnectd()) {
                        sleep(10000);
                        continue;
                    }
                    heartbeatWrite();
                    Thread.sleep(1000);
                } catch (Exception ex) {
                    log.error("心跳写处理异常", ex);
                }
            }
        });
        /**
         * 扫码称重口业务逻辑处理
         * */
        executorService.execute(() -> {
            while (true) {
                try {
                    if (!siemensPlcDriver.siemensHelper.isConnectd()) {
                        sleep(10000);
                        continue;
                    }
                    portProcess();
                    sleep(100);
                } catch (Exception ex) {
                    log.error("入库口任务处理异常", ex);
                }
            }
        });
        /**
         * 输送线业务处理逻辑
         * */
        executorService.execute(() -> {
            while (true) {
                try {
                    if (!siemensPlcDriver.siemensHelper.isConnectd()) {
                        //log.error("{}离线，暂停输送线任务", plcName);
                        sleep(10000);
                        continue;
                    }
                    transmissionProcess();
                    sleep(100);
                } catch (Exception ex) {
                    log.error("输送线任务处理异常", ex);
                }
            }
        });
    }

    private void transmissionProcess() {
        try {
            if (!CollectionUtils.isEmpty(siemensPlcDriver.driverData.getTransLinePlcMap())) {
                siemensPlcDriver.driverData.getTransLinePlcMap().entrySet().parallelStream().forEach(transmissionPlcEntry->{
                    List<WcsLineTask> lineTasks = lineTaskService .getUnfinishedByTransmissionList();
                    if (CollectionUtils.isEmpty(lineTasks)) {
                        return;
                    }
                    TransmissionPlc transmissionPlc = transmissionPlcEntry.getValue();
                    //任务开始
                    startTransmissionTask(lineTasks, transmissionPlc);
                    //任务结束
                    finishTransmissionTask(lineTasks, transmissionPlc);
                    //任务下发
                    sendTransmissionTask(lineTasks, transmissionPlc);
                });
            }
        } catch (Exception ex) {
            log.error("transmissionProcess 异常", ex);
        }
    }

    private void sendTransmissionTask(List<WcsLineTask> lineTasks, TransmissionPlc tPlc) {
        WcsLineTask lineTask = ListHelper.firstOrDefault(lineTasks, t ->
                tPlc.getCoord().equals(t.getSource()) && TaskStatus.READY.getTaskProgress() == t.getTaskStatus()
        );
        if (null == lineTask) {
            return;
        }
        String taskJsonLog = JSON.toJSONString(lineTask);
        try {
            //信号检查
            transmissionCheck(tPlc,lineTask, TaskStatus.READY.getTaskProgress());
            //写PLC指令
            mcsWritePlc(lineTask);
            //退回直接任务完成
            if (WcsTaskTypeEnums.SEND_BACK.getValue() == lineTask.getTaskType()) {
                log.info("{}输送线指令下发退回成功，更新任务状态为3，任务信息：{}", tPlc.getCoord(), taskJsonLog);
                lineTask.setTaskStatus(TaskStatus.FINISH.getTaskProgress());
            } else {
                log.info("{}输送线指令下发成功，更新任务状态为1，任务信息：{}", tPlc.getCoord(), taskJsonLog);
                lineTask.setTaskStatus(TaskStatus.SEND.getTaskProgress());
                lineTask.setRemark("");
            }
            lineTaskService.updateById(lineTask);
            //任务日志
            String desc = WcsTaskTypeEnums.ofValue(lineTask.getTaskType()) != null ? WcsTaskTypeEnums.ofValue(lineTask.getTaskType()).getDesc() : "未知";
            String msg = lineTask.getPalletNo() + " " + desc + "任务下发PLC成功, " + lineTask.getSource() + ">>" + lineTask.getTarget();
            String content = "【"+desc+"】"+msg;
            deviceLogService.genCoordLog(lineTask.getSource(),content);
        } catch (WmsBusinessException e) {
            //log.warn("{}有待发送任务，{}，任务信息：{}", tPlc.getCoord(), e.getMessage(), taskJsonLog);
            overTask(lineTask,e.getMessage());
        }
    }

    private void mcsWritePlc(WcsLineTask mcsTaskInfo) throws WmsBusinessException {
        WcsTaskTypeEnums taskType = WcsTaskTypeEnums.ofValue(mcsTaskInfo.getTaskType());
        switch (Objects.requireNonNull(taskType)) {
            case INBOUND_T:
            case OUTBOUND_T:
            case INBOUND_S:
            case OUTBOUND_S:
                mcsWriteTransmissionPlc(mcsTaskInfo);
                break;
            case SEND_BACK:
                mcsWriteTransmissionBackPlc(mcsTaskInfo);
                break;
            default:
                log.error("任务类型{}不存在", mcsTaskInfo.getTaskType());
                throw new WmsBusinessException("未知的任务类型");
        }
    }

    private void mcsWriteTransmissionBackPlc(WcsLineTask lineTask) {
        BasePlc sourceStation = getPlcPropertyByCoordAndType(lineTask.getSource());
        if (null == sourceStation) {
            throw new WmsBusinessException(String.format("%s PLC写值地址不存在，请检查配置", lineTask.getSource()));
        }
        boolean result = writeTransmissionPlcAddress(
                lineTask.getTaskNo(),
                sourceStation.getWriteAddress(),
                1,
                lineTask.getTaskType(),
//                lineTask.getSerialNo()
                0
        );
        if (!result) {
            throw new WmsBusinessException("PLC写入失败，请查看PLC日志");
        }
    }



    private void mcsWriteTransmissionPlc(WcsLineTask lineTask) {
        BasePlc sourceDevice = getPlcPropertyByCoordAndType(lineTask.getSource());
        if (null == sourceDevice) {
            throw new WmsBusinessException(String.format("%s PLC写值地址不存在，请检查配置", lineTask.getSource()));
        }
        BasePlc targetDevice = getPlcPropertyByCoordAndType(lineTask.getTarget());
        if (null == targetDevice) {
            throw new WmsBusinessException(String.format("%s PLC写值地址不存在，请检查配置", lineTask.getTarget()));
        }
        boolean result = writeTransmissionPlcAddress(
                lineTask.getTaskNo(),
                sourceDevice.getWriteAddress(),
                targetDevice.getStation(),
                lineTask.getTaskType(),
//                lineTask.getSerialNo()
                0
        );
        if (!result) {
            throw new WmsBusinessException("PLC写入失败，请查看PLC日志");
        }
    }

    private boolean writeTransmissionPlcAddress(String taskNo, String address, int targetDevice, int type, int mcsId) {
        byte[] bytes = PlcUtils.taskTransmissionByte(targetDevice, type, mcsId);
        Map<String, Object> map = Maps.newHashMap();
        map.put("目标站", targetDevice);
        map.put("任务类型", type);
        map.put("任务号", mcsId);
        map.put("Wms任务号", taskNo);
        return siemensPlcDriver.savePlcLog(address, bytes, map);
    }


    private BasePlc getPlcPropertyByCoordAndType(String coordInput) {
        //获取输送线点位
        if (!CollectionUtils.isEmpty(siemensPlcDriver.driverData.getTransLinePlcMap())) {
            return siemensPlcDriver.driverData.getTransLinePlcMap().get(coordInput);
        }
        return null;
    }


    private void finishTransmissionTask(List<WcsLineTask> lineTasks, TransmissionPlc tPlc) {
        //检查任务结束
        List<WcsLineTask> where = ListHelper.where(lineTasks, t ->
                tPlc.getCoord().equals(t.getTarget()) &&
                        ArrayUtils.contains(new int[]{TaskStatus.SEND.getTaskProgress(), TaskStatus.START.getTaskProgress()}, t.getTaskStatus()));
        if (CollectionUtils.isEmpty(where)) {
            return;
        }
        finishLogic(where.get(0), tPlc);
    }

    private void finishLogic(WcsLineTask lineTask, TransmissionPlc tPlc) {
        String taskJsonLog = JSON.toJSONString(lineTask);
        try {
            //信号检查
            transmissionCheck(tPlc, lineTask, TaskStatus.START.getTaskProgress());
            //GCS点位清除
//            gcsClearTask(tPlc, mcsTaskInfo);
            //任务回告
//            finishTaskReport(mcsTaskInfo);
            //修改任务状态为完成
            lineTask.setTaskStatus(TaskStatus.FINISH.getTaskProgress());
            lineTask.setRemark("");
            lineTaskService.updateById(lineTask);
            log.info("{}任务回告完成，更新任务状态为3，任务信息：{}", lineTask.getTaskNo(), taskJsonLog);
            //任务日志
            String desc = WcsTaskTypeEnums.ofValue(lineTask.getTaskType()) != null ? WcsTaskTypeEnums.ofValue(lineTask.getTaskType()).getDesc() : "未知";
            String msg = lineTask.getPalletNo() + " " + desc + "任务完成，目标位置 " + tPlc.getCoord() + " 到位信号=" + tPlc.getInPlaceNum() + "，任务号=" + tPlc.getDeviceNum();
            String content = "【"+desc+"】"+msg;
            deviceLogService.genCoordLog(tPlc.getCoord(),content);
        } catch (WmsBusinessException e) {
            //log.warn("{}有已开始的任务，{}，任务信息：{}", tPlc.getCoord(), e.getMessage(), taskJsonLog);
            overTask(lineTask, e.getMessage());
        }
    }

    private void startTransmissionTask(List<WcsLineTask> mcsTaskInfoList, TransmissionPlc tPlc) {
        //检查任务开始
        WcsLineTask lineTask = ListHelper.firstOrDefault(mcsTaskInfoList, t ->
                tPlc.getCoord().equals(t.getSource()) && TaskStatus.SEND.getTaskProgress() == t.getTaskStatus()
        );
        //无任务
        if (null == lineTask) {
            return;
        }
        String taskJsonLog = JSON.toJSONString(lineTask);
        try {
            //信号检查
            transmissionCheck(tPlc, lineTask, TaskStatus.SEND.getTaskProgress());
            //GCS点位清除
//            gcsClearTask(tPlc, lineTask);
            //任务回告
//            startTaskReport(mcsTaskInfo);
            //修改任务状态为开始
            lineTask.setTaskStatus(TaskStatus.START.getTaskProgress());
            lineTask.setRemark("");
            lineTaskService.updateById(lineTask);
            log.info("{}任务回告开始，更新任务状态为2，任务信息：{}", lineTask.getTaskNo(), taskJsonLog);
            //任务日志
            String desc = WcsTaskTypeEnums.ofValue(lineTask.getTaskType()) != null ? WcsTaskTypeEnums.ofValue(lineTask.getTaskType()).getDesc() : "未知";
            String msg = lineTask.getPalletNo() + " " + desc + "任务开始，源位置 " + tPlc.getCoord() + " 到位信号=" + tPlc.getInPlaceNum() + "，任务号=" + tPlc.getDeviceNum();
            String content = "【"+desc+"】"+msg;
            deviceLogService.genCoordLog(tPlc.getCoord(),content);
        } catch (WmsBusinessException e) {
            //log.warn("{}有已发送指令的任务，{}，任务信息：{}", tPlc.getCoord(), e.getMessage(), taskJsonLog);
            overTask(lineTask,e.getMessage());
        }
    }

    private void overTask(WcsLineTask lineTask, String msg) {
        if (msg.equals(lineTask.getRemark())) {
            return;
        }
        lineTask.setRemark(msg);
        lineTaskService.updateById(lineTask);
    }

    private void transmissionCheck(TransmissionPlc tPlc, WcsLineTask lineTask, int status) {
//        int mcsId = lineTask.getSerialNo();
        if (!tPlc.isAuto()) {
            throw new WmsBusinessException(String.format("%s 不是自动状态，等待系统切换为自动状态", tPlc.getStation()));
        }
        if (tPlc.getStatus() != 2) {
            throw new WmsBusinessException(String.format("%s 不是联机自动状态，等待设备切换为联机自动状态", tPlc.getStation()));
        }
        if (TaskStatus.SEND.getTaskProgress() == status) {
            //任务开始检查
            if (tPlc.getInPlaceNum() == 1) {
                throw new WmsBusinessException(String.format("%s 有载位信号，等待容器移出", tPlc.getStation()));
            }
//            if (tPlc.getDeviceNum() == mcsId) {
//                throw new WmsBusinessException(String.format("%s PLC任务号未清空，等待容器移出", tPlc.getStation()));
//            }
        } else if (TaskStatus.START.getTaskProgress() == status) {
            //任务完成检查
            if (tPlc.getInPlaceNum() == 0) {
                throw new WmsBusinessException(String.format("%s 无载位信号，等待容器送达", tPlc.getStation()));
            }
//            if (tPlc.getDeviceNum() != mcsId) {
//                throw new WmsBusinessException(String.format("%s PLC任务号%s与MCS任务号%s不一致，等待容器送达", tPlc.getStation(), tPlc.getDeviceNum(), mcsId));
//            }
        } else if (TaskStatus.READY.getTaskProgress() == status) {
            //任务下发检查
            if (tPlc.getInPlaceNum() == 0) {
                throw new WmsBusinessException(String.format("%s 无载位信号，请检查容器/光电是否正常", tPlc.getStation()));
            }
            if (tPlc.getWorkStatus() != 0) {
                throw new WmsBusinessException(String.format("%s 不是就绪状态，等待设备就绪", tPlc.getStation()));
            }
        }
    }

    private void portProcess() {
        try {
            if (!CollectionUtils.isEmpty(siemensPlcDriver.driverData.getPortPlcMap())) {
                siemensPlcDriver.driverData.getPortPlcMap().entrySet().parallelStream().forEach(portPlcEntry -> {
                    inBoundBind(portPlcEntry.getValue());
                });
            }
        } catch (Exception ex) {
            log.error("portProcess 异常", ex);
        }
    }


    private void inBoundBind(PortPlc portPlc) {
        TransmissionPlc transmissionPlc = siemensPlcDriver.driverData.getTransLinePlcMap().get(portPlc.getCoord());
        //无到位信号
        if (transmissionPlc == null || transmissionPlc.getInPlaceNum() == 0) {
            return;
        }
        //流水号没变
        if (portPlc.getPlcNum() == 0 || !portPlc.isChange()) {
            log.warn("{}处理入库申请等待，未检测到流水号变化，PLC状态{}", portPlc.getCoord(), JSON.toJSONString(portPlc));
            return;
        }
        //条码为空
        if (StringUtils.isBlank(portPlc.getBcrCode())) {
            log.warn("{}处理入库申请等待，条码为空", portPlc.getCoord());
            return;
        }
        //外形检测数据异常
        if (portPlc.getDetection() == 0) {
            log.warn("{}处理入库申请等待，外形检测数据为0", portPlc.getCoord());
            return;
        }
        if (!portPlc.isAuto()) {
            log.warn("{}处理入库申请等待，设备不是自动状态", portPlc.getCoord());
            return;
        }
        boolean b = saveBindData(portPlc);
        if (b) {
            portPlc.setChange(false);
            log.info("{}处理入库申请成功，PLC状态{}", portPlc.getCoord(), JSON.toJSONString(portPlc));

            String msg = "扫码上报完成：容器号" + portPlc.getBcrCode() + "，重量" + portPlc.getWeight() + "，尺寸" + portPlc.getDetection() + "，流水号" + portPlc.getPlcNum();
            deviceLogService.genCoordLog(portPlc.getCoord(),msg);
        }
    }

    private boolean saveBindData(BasePlc basePlc) {
        boolean b = true;
        List<WcsContainerBind> containerBinds = containerBindService.getUnfinishedList(basePlc.getCoord());
        if (CollectionUtils.isEmpty(containerBinds)) {
            if(basePlc instanceof PortPlc){
                PortPlc portPlc = (PortPlc)basePlc;
                WcsContainerBind mcsContainerBind = new WcsContainerBind();
                mcsContainerBind.setTpCode(portPlc.getBcrCode());
                mcsContainerBind.setType(WcsBindTypeEnums.PPT.getValue());
                mcsContainerBind.setSource(portPlc.getCoord());
                mcsContainerBind.setStatus(WcsBindStatusEnums.WJB.getValue());
                mcsContainerBind.setDetection(portPlc.getDetection());
                mcsContainerBind.setWeight(StringUtils.isEmpty(portPlc.getWeight()) ? "1" : portPlc.getWeight());
                mcsContainerBind.setPriority(5);
                mcsContainerBind.setTarget("1");
                b = containerBindService.save(mcsContainerBind);
            }
        }
        return b;
    }

    private void heartbeatWrite() {
        try {
            if (beatValue == 0) {
                beatValue++;
            } else {
                beatValue = 0;
            }
            if (!CollectionUtils.isEmpty(siemensPlcDriver.driverData.getHeartBeatPlcMap())) {
                siemensPlcDriver.driverData.getHeartBeatPlcMap().entrySet().parallelStream().forEach(heartBeatPlcEntry -> {
                    siemensPlcDriver.write(heartBeatPlcEntry.getValue().getWriteAddress(), beatValue);
                });
            }
        } catch (Exception ex) {
            log.error("heartbeatWrite 异常", ex);
        }
    }

    private void heartbeatProcess() {
        try {
            if (!CollectionUtils.isEmpty(siemensPlcDriver.driverData.getHeartBeatPlcMap())) {
                siemensPlcDriver.driverData.getHeartBeatPlcMap().entrySet().parallelStream().forEach(heartBeatPlcEntry -> {
                    HeartBeatPlc heartBeatPlc = heartBeatPlcEntry.getValue();
                    if (!heartBeatPlc.isChange()) {
                        siemensPlcDriver.reconnect();
                    } else {
                        heartBeatPlc.setChange(false);
                    }
                });
            }
        } catch (Exception ex) {
            log.error("heartbeatProcess 异常", ex);
        }
    }

}
